﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Text;

namespace EyeOfTheWorld
{
    public class GZipBrowser : WebClient
    {
        protected override WebRequest GetWebRequest(Uri address)
        {
            HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
            request.AutomaticDecompression = DecompressionMethods.GZip |
                                             DecompressionMethods.Deflate;
            return request;
        }
    }

    public class WebSpider
    {
        // SPIDER FIELDSET
        #region
        WebClient web = new WebClient();
        GZipBrowser gzipWeb = new GZipBrowser();
        Encoding encoding = Encoding.Default;
        #endregion

        // CONSTRUCTOR
        public WebSpider()
        {
            Encoding = Encoding.UTF8;
            web.Encoding = Encoding;
            gzipWeb.Encoding = Encoding;
        }

        // PROPERTIES
        #region
        public Encoding Encoding
        {
            get { return this.encoding; }
            set { this.encoding = value; }
        }
        #endregion

        // MAIN METHOD
        public string GetData(string root, int depth, int namesLevel)
        {
            List<List<Page>> pagesLeveled = new List<List<Page>>();
            List<string> vsitedPages = new List<string>();
            StringBuilder nodes = new StringBuilder();
            StringBuilder edges = new StringBuilder();
            pagesLeveled.Add(new List<Page>());
            Page rootPage = new Page(root);
            //rootPage.Number = count++;
            pagesLeveled[0].Add(rootPage);
            //vsitedPages.Add(root);
            //nodes.Append(string.Format("{0}|{1}|{2}|", rootPage.Address, rootPage.Address, 0));

            for (int level = 0; level < depth; )
            {
                List<Page> currentLevel = pagesLeveled[level];

                pagesLeveled.Add(new List<Page>());
                level++;
                foreach (Page page in currentLevel)
                {
                    vsitedPages.Add(page.Address);
                    string[] pageLinks = ObtainCurrentPageHTMLContent(page.Address, false).GetLinks(page.Address);
                    int connectionsCount = 0;
                    foreach (string link in pageLinks)
                    {
                        string pageDestination = link.CompleteLinkBody(page.Address);

                        //edges.Append(string.Format("{0}|{1}|1|", page.Number, link.ToPage.Number));
                        if (!vsitedPages.Contains(pageDestination))
                        {
                            Page destination = new Page(pageDestination);
                            //destination.Number = count++;
                            vsitedPages.Add(pageDestination);
                            connectionsCount++;
                            edges.Append(string.Format("{0}|{1}|", vsitedPages.IndexOf(page.Address), vsitedPages.IndexOf(destination.Address)));
                            pagesLeveled[level].Add(new Page(pageDestination));
                        }
                    }
                    if (connectionsCount > namesLevel)
                    {
                        if (level == 1)
                        {
                            page.Label = page.Address;
                        }
                        else if (depth > 1 && level - 1 < depth)
                        {
                            page.Label = page.Address;
                        }
                    }
                    //nodes.Append(string.Format("{0}|{1}|{2}|", page.Address, connectionsCount > 20 ? page.Address : "", level));
                }
            }

            for (int level = 0; level < pagesLeveled.Count; level++)
            {
                foreach (Page page in pagesLeveled[level])
                {
                    nodes.Append(string.Format("{0}|{1}|{2}|", page.Address, page.Label, level));
                }
            }
            return string.Format("{0}${1}", nodes.ToString().TrimEnd('|'), edges.ToString().TrimEnd('|'));
        }

        // METHODS

        // DOWNLOADS HTML CODE OF THE GIVEN PAGE ADDRESS
        private string ObtainCurrentPageHTMLContent(string pageCurrentAddress, bool useGzip)
        {
            if (useGzip) // MANAGES GZIPED DATA
            {
                return gzipWeb.DownloadString(pageCurrentAddress);
            }
            else
            {
                try
                {
                    return web.DownloadString(pageCurrentAddress);
                }
                catch (Exception)
                {
                    return string.Empty;
                }
            }
        }

        // CREATES A DYNAMIC REQUEST AND COMPARES CALLING LINK WITH FINAL DESTINATION
        private string CheckForRedirect(string addressFrom)
        {
            Uri currentAddress = new Uri(addressFrom);
            string finalURL = string.Empty;

            WebRequest currentWebRequest = WebRequest.Create(addressFrom);
            WebResponse myWebResponse = currentWebRequest.GetResponse();

            if (currentAddress.Equals(myWebResponse.ResponseUri.AbsolutePath))
            {
                finalURL = addressFrom.Replace(" ", "%20");
            }
            else
            {
                finalURL = myWebResponse.ResponseUri.ToString().Replace(" ", "%20");
            }
            myWebResponse.Close();
            return finalURL;
        }
    }
}